#pragma once 
#include <iostream>
#include <functional>
#include <string>
#include <unordered_map>
#include "sock.hpp"
#include "epoll.hpp"
#include "util.hpp"
#include "protocol.hpp"

static const int defaultport = 8888;
//static const int defaultfd = -1;
static const int maxnum = 128;
class connection;
class tcpserver;
using func_t = std::function< void (connection*) >;

struct connection{
    int _sockfd;
    std::string _inbuffer;
    std::string _outbuffer;
    func_t _recver;
    func_t _sender;
    func_t _excepter;
    bool IsEpollOut;
    
    connection() :_sockfd(defaultfd),_recver(nullptr),_sender(nullptr),_excepter(nullptr)
    {}
    ~connection(){
        if(_sockfd != defaultfd) close(_sockfd);
    }
    void Register(int sockfd,func_t recver,func_t sender,func_t excepter){
        _sockfd = sockfd;
        _recver = recver;
        _sender = sender;
        _excepter = excepter;
    }
};
//基于epoll ET模式的服务器
class tcpserver{
public:
    tcpserver(uint16_t port = defaultport) :_port(port)
    {}
    ~tcpserver(){
        delete[] _events;
    }
public:
    void Init(){
        //
        _events = new epoll_event[maxnum];
        //套接字初始化
        _sock.Socket();
        _sock.Bind(_port);
        _sock.Listen();
        //创建epoll模型
        _epoll.Create();
        //向epoll中添加监听套接字
        AddConnection(_sock.Getsock(),EPOLLIN|EPOLLET , std::bind(&tcpserver::Accepter,this,std::placeholders::_1), nullptr,nullptr);
    }
    void Start(){
        for(;;){
            int timeout = -1;
            Loop(timeout);
        }
    }
private:
    void Loop(int timeout){
        int n = _epoll.Wait(_events,maxnum);
        for(int i = 0; i < n ; i++){
            int sockfd = _events[i].data.fd;
            short events = _events[i].events;
            if(events & EPOLLIN){
                //EPOLLIN事件
                _connections[sockfd]->_recver(_connections[sockfd]);
            }
            if(events & EPOLLOUT){
                //EPOLLOUT事件
                _connections[sockfd]->_sender(_connections[sockfd]);
            }
            if((events & EPOLLERR) | (events & EPOLLHUP)){
                _connections[sockfd]->_excepter(_connections[sockfd]);
            }
        }
    }
    void AddConnection(int sockfd,uint32_t events,func_t recver,func_t sender,func_t excepter){
        // 将sockfd调整为nonblock模式
        Util::SetNonBlock(sockfd);
        //创建并注册connection对象
        connection* ctp = new connection;
        ctp->Register(sockfd,recver,sender,excepter);
        //将connection管理起来
        _connections.insert(std::make_pair(sockfd,ctp));
        //将connection对象的fd添加到epoll模型中
        _epoll.AddEvent(sockfd,events);
        logmessage(NORMAL,"添加connection成功,fd:%d",sockfd);
    }
    void Accepter(connection* ctp){
        for(;;){
            int fd = _sock.Accept();
            if(fd >= 0){
                //添加链接
                AddConnection(fd,EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLET,\
                    std::bind(&tcpserver::Reader,this,std::placeholders::_1),\
                    std::bind(&tcpserver::Sender,this,std::placeholders::_1),\
                    std::bind(&tcpserver::Excepter,this,std::placeholders::_1)
                ); //TODO
            }else{
                if(errno == EAGAIN | errno == EWOULDBLOCK) break; //处理完毕
                if(errno == EINTR) continue;
                else break;
            }
        }
    }
    void Service(connection* ctp){
        //循环读取完整报文，每获取一个完整报文，就构建响应，再发送
        std::string onepack;
        while(Getcompletemessage(ctp->_sockfd,ctp->_inbuffer,onepack)){ //一个完整的报文已经在onepack中
            logmessage(DEBUG,"onepack:%s",onepack.c_str());
            onepack = deleteheader(onepack); //
            logmessage(DEBUG,"onepack:%s",onepack.c_str());
            //logmessage(DEBUG,"%s",onepack.c_str());
            Request req;
            req.unserialization(onepack);
            Response res;
            handler(req,res);
            std::string message;
            res.serialization(&message);
            ctp->_outbuffer = addheader(message); //不断的将完整报文放在message中
        }
        //已经将inbuffer中的完整报文全部读取到outbuffer中
        logmessage(DEBUG,"Service succuss,%s",ctp->_outbuffer.c_str());
        ctp->_sender(ctp);

    }
    void Sender(connection* ctp){
        //任务就是将outbuffer中的所有数据发送
        while(true){
            int s = send(ctp->_sockfd,ctp->_outbuffer.c_str(),ctp->_outbuffer.size(),0);
            if(s > 0){
                ctp->_outbuffer.erase(0,s); //发送成功后就删除outbuffer中发送的缓存
                if(ctp->_outbuffer.empty()) break; //如果outbuffer空了就return
            }else{
                //发送失败，或者发送缓冲区已经满了
                if(errno == EAGAIN | errno == EWOULDBLOCK){
                    //此时发送缓冲区已经满了，就要设置fd的事件加上EPOLLOUT
                    ctp->IsEpollOut = true;
                    _epoll.ModEvent(ctp->_sockfd,EPOLLIN|EPOLLET|EPOLLOUT);
                    return;
                }
                if(errno == EINTR) {
                    continue;
                }
                logmessage(ERROR,"send faild");
                ctp->_excepter(ctp);
                return;
            }
        }
        if(ctp->IsEpollOut) _epoll.ModEvent(ctp->_sockfd,EPOLLIN|EPOLLET);
        
    }
    void Reader(connection* ctp){
        char buffer[1024];
        while(true){
            ssize_t s = recv(ctp->_sockfd,buffer,sizeof(buffer)-1,0);
            if(s > 0){
                //读到数据
                buffer[s] = 0;
                logmessage(DEBUG,"client say:%s",buffer);
                ctp->_inbuffer += buffer;
                logmessage(DEBUG,"%s",ctp->_inbuffer.c_str());
                Service(ctp);// 每成功读一次就去Service中检验是否是完整报文，再进一步处理
            }else if(s==0){
                //对方文件描述符关闭
                logmessage(NORMAL,"client quit");
                ctp->_excepter(ctp);
                break;
            }else{
                //1.缓冲区无数据 或者 读到错误
                if(errno == EAGAIN | errno == EWOULDBLOCK) break; //处理完毕
                if(errno == EINTR) continue;
                else {
                    logmessage(ERROR,"Read faild");
                    ctp->_excepter(ctp);
                    break;
                }
            }
        }   
    }
    void Excepter(connection* cpt){
        //1.关闭文件描述符，从epoll中移除，从connections中移除，清楚connection对象
        
        _epoll.DelEvent(cpt->_sockfd);
        close(cpt->_sockfd);
        _connections.erase(cpt->_sockfd);
        logmessage(NORMAL,"关闭链接成功fd:%d",cpt->_sockfd);
        delete cpt;
    }
private:
    uint16_t _port;
    Sock _sock;
    Epoll _epoll;
    std::unordered_map<int,connection*> _connections;
    struct epoll_event* _events;
};